/* 
 * File:   Loader3DS.cpp
 * Author: gilvolpe
 * 
 * Created on June 8, 2013, 9:59 AM
 */

#include "Loader3DS.h"


/*
 * Funciona os para o space ship
 */
void Loader3DS::read3DSextension(obj_type_ptr obj,char *elem3DS) {
    
    Loader3DS::load3DS(obj, elem3DS);
    
    Loader3DS::setNormals(obj);
}

long Loader3DS::filelength(int f) {
    struct stat buf;

    fstat(f, &buf);
    return (buf.st_size);

}

char Loader3DS::load3DS(obj_type_ptr p_object, char *p_filename) {
    int i; //Index variable

    FILE *l_file; //File pointer

    unsigned short l_chunk_id; //Chunk identifier
    unsigned int l_chunk_lenght; //Chunk lenght

    unsigned char l_char; //Char variable
    unsigned short l_qty; //Number of elements in each chunk

    unsigned short l_face_flags; //Flag that stores some face information

    if ((l_file = fopen(p_filename, "rb")) == NULL) return 0; //Open the file

    while (ftell(l_file) < filelength(fileno(l_file))) //Loop to scan the whole file
        //while(!EOF)
    {
        //getch(); //Insert this command for debug (to wait for keypress for each chuck reading)

        fread(&l_chunk_id, 2, 1, l_file); //Read the chunk header
        //        printf("ChunkID: %x\n", l_chunk_id);
        fread(&l_chunk_lenght, 4, 1, l_file); //Read the lenght of the chunk
        //        printf("ChunkLenght: %x\n", l_chunk_lenght);

        switch (l_chunk_id) {
                //----------------- MAIN3DS -----------------
                // Description: Main chunk, contains all the other chunks
                // Chunk ID: 4d4d 
                // Chunk Lenght: 0 + sub chunks
                //-------------------------------------------
            case 0x4d4d:
                break;

                //----------------- EDIT3DS -----------------
                // Description: 3D Editor chunk, objects layout info 
                // Chunk ID: 3d3d (hex)
                // Chunk Lenght: 0 + sub chunks
                //-------------------------------------------
            case 0x3d3d:
                break;

                //--------------- EDIT_OBJECT ---------------
                // Description: Object block, info for each object
                // Chunk ID: 4000 (hex)
                // Chunk Lenght: len(object name) + sub chunks
                //-------------------------------------------
            case 0x4000:
                i = 0;
                do {
                    fread(&l_char, 1, 1, l_file);
                    p_object->name[i] = l_char;
                    i++;
                } while (l_char != '\0' && i < 20);
                break;

                //--------------- OBJ_TRIMESH ---------------
                // Description: Triangular mesh, contains chunks for 3d mesh info
                // Chunk ID: 4100 (hex)
                // Chunk Lenght: 0 + sub chunks
                //-------------------------------------------
            case 0x4100:
                break;

                //--------------- TRI_VERTEXL ---------------
                // Description: Vertices list
                // Chunk ID: 4110 (hex)
                // Chunk Lenght: 1 x unsigned short (number of vertices) 
                //             + 3 x float (vertex coordinates) x (number of vertices)
                //             + sub chunks
                //-------------------------------------------
            case 0x4110:
                fread(&l_qty, sizeof (unsigned short), 1, l_file);
                p_object->vertices_qty = l_qty;
                //                printf("Number of vertices: %d\n", l_qty);
                for (i = 0; i < l_qty; i++) {
                    fread(&p_object->vertex[i].x, sizeof (float), 1, l_file);
                    //                    printf("Vertices list x: %f\n", p_object->vertex[i].x);
                    fread(&p_object->vertex[i].y, sizeof (float), 1, l_file);
                    //                    printf("Vertices list y: %f\n", p_object->vertex[i].y);
                    fread(&p_object->vertex[i].z, sizeof (float), 1, l_file);
                    //                    printf("Vertices list z: %f\n", p_object->vertex[i].z);
                }
                break;

                //--------------- TRI_FACEL1 ----------------
                // Description: Polygons (faces) list
                // Chunk ID: 4120 (hex)
                // Chunk Lenght: 1 x unsigned short (number of polygons) 
                //             + 3 x unsigned short (polygon points) x (number of polygons)
                //             + sub chunks
                //-------------------------------------------
            case 0x4120:
                fread(&l_qty, sizeof (unsigned short), 1, l_file);
                p_object->polygons_qty = l_qty;
                //                printf("Number of polygons: %d\n", l_qty);
                for (i = 0; i < l_qty; i++) {
                    fread(&p_object->polygon[i].a, sizeof (unsigned short), 1, l_file);
                    //                    printf("Polygon point a: %d\n", p_object->polygon[i].a);
                    fread(&p_object->polygon[i].b, sizeof (unsigned short), 1, l_file);
                    //                    printf("Polygon point b: %d\n", p_object->polygon[i].b);
                    fread(&p_object->polygon[i].c, sizeof (unsigned short), 1, l_file);
                    //                    printf("Polygon point c: %d\n", p_object->polygon[i].c);
                    fread(&l_face_flags, sizeof (unsigned short), 1, l_file);
                    //                    printf("Face flags: %x\n", l_face_flags);
                }
                break;

                //------------- TRI_MAPPINGCOORS ------------
                // Description: Vertices list
                // Chunk ID: 4140 (hex)
                // Chunk Lenght: 1 x unsigned short (number of mapping points) 
                //             + 2 x float (mapping coordinates) x (number of mapping points)
                //             + sub chunks
                //-------------------------------------------
            case 0x4140:
                fread(&l_qty, sizeof (unsigned short), 1, l_file);
                for (i = 0; i < l_qty; i++) {
                    fread(&p_object->mapcoord[i].u, sizeof (float), 1, l_file);
                    //                    printf("Mapping list u: %f\n", p_object->mapcoord[i].u);
                    fread(&p_object->mapcoord[i].v, sizeof (float), 1, l_file);
                    //                    printf("Mapping list v: %f\n", p_object->mapcoord[i].v);
                }
                break;

                //----------- Skip unknow chunks ------------
                //We need to skip all the chunks that currently we don't use
                //We use the chunk lenght information to set the file pointer
                //to the same level next chunk
                //-------------------------------------------
            default:
                fseek(l_file, l_chunk_lenght - 6, SEEK_CUR);
        }
    }

    fclose(l_file); // Closes the file stream
    return (1); // Returns ok
}

void Loader3DS::setNormals(obj_type_ptr p_object) {

    int i;
    Vector<GLfloat> l_vect1, l_vect2, l_vect3; //Some local vectors

    int l_connections_qty[MAX_VERTICES]; //Number of poligons around each vertex

    // Resetting vertices' normals...
    for (i = 0; i < p_object->vertices_qty; i++) {
        
        p_object->normal[i].x = 0.0;
        p_object->normal[i].y = 0.0;
        p_object->normal[i].z = 0.0;
        l_connections_qty[i] = 0;
    }

    for (i = 0; i < p_object->polygons_qty; i++) {
        
        l_vect1.setPosition(p_object->vertex[p_object->polygon[i].a].x, 
                            p_object->vertex[p_object->polygon[i].a].y,
                            p_object->vertex[p_object->polygon[i].a].z);
        
        l_vect2.setPosition(p_object->vertex[p_object->polygon[i].b].x, 
                            p_object->vertex[p_object->polygon[i].b].y,
                            p_object->vertex[p_object->polygon[i].b].z);
        
        l_vect3.setPosition(p_object->vertex[p_object->polygon[i].c].x, 
                            p_object->vertex[p_object->polygon[i].c].y,
                            p_object->vertex[p_object->polygon[i].c].z);
        
        Vector<GLfloat> l_vect_b1(l_vect1,l_vect2);
        Vector<GLfloat> l_vect_b2(l_vect1,l_vect3);
        
        Vector<GLfloat> l_normal = l_vect_b1.vectorProduct(l_vect_b2);
        
        
        l_normal.normalize();
//        cout << i << " " << l_normal.getX() << " " << l_normal.getY() << " " << l_normal.getZ() << endl; 
        
        l_connections_qty[p_object->polygon[i].a] += 1; // For each vertex shared by this polygon we increase the number of connections
        l_connections_qty[p_object->polygon[i].b] += 1;
        l_connections_qty[p_object->polygon[i].c] += 1;

        p_object->normal[p_object->polygon[i].a].x += l_normal.getX(); // For each vertex shared by this polygon we add the polygon normal
        p_object->normal[p_object->polygon[i].a].y += l_normal.getY();
        p_object->normal[p_object->polygon[i].a].z += l_normal.getZ();
        
        p_object->normal[p_object->polygon[i].b].x += l_normal.getX();
        p_object->normal[p_object->polygon[i].b].y += l_normal.getY();
        p_object->normal[p_object->polygon[i].b].z += l_normal.getZ();
        
        p_object->normal[p_object->polygon[i].c].x += l_normal.getX();
        p_object->normal[p_object->polygon[i].c].y += l_normal.getY();
        p_object->normal[p_object->polygon[i].c].z += l_normal.getZ();
    }

    for (i = 0; i < p_object->vertices_qty; i++) {
        if (l_connections_qty[i] > 0) {
            p_object->normal[i].x /= l_connections_qty[i]; // Let's now average the polygons' normals to obtain the vertex normal!
            p_object->normal[i].y /= l_connections_qty[i];
            p_object->normal[i].z /= l_connections_qty[i];
        }
    }

}
